//---------------------------------------------------------------------
// File: EventLogCheckStep.cs
// 
// Summary: 
//
//---------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
// PURPOSE.
//---------------------------------------------------------------------

namespace Microsoft.Services.BizTalkApplicationFramework.BizUnit
{
	using System;
	using System.Diagnostics;
	using System.Xml;
	using System.Text.RegularExpressions;

	/// <summary>
	/// The EventLogCheckStep test step looks for an event log entry. Note: this test step only looks for the event log entry during the time period of the test case.
	/// </summary>
	/// <remarks>
	/// The following shows an example of the Xml representation of this test step.
	/// 
	/// <code escaped="true">
	/// <TestStep assemblyPath="" typeName="Microsoft.Services.BizTalkApplicationFramework.BizUnit.EventLogCheckStep">
	///		<DelayBeforeCheck>0</DelayBeforeCheck> <!-- Optional, seconds to delay performing check -->
	///		<Machine>UKBTSTEST01</Machine>
	///		<EventLog>Application</EventLog>
	///		<Source>BizTalk Governor</Source>
	///		<Type>Error</Type>
	///		<EventId>10002</EventId>
	///		<ValidationRegex>The BizTalk Governor disabled the receive location: GovernorIn</ValidationRegex>
	///		<FailIfFound>False</FailIfFound>
	/// </TestStep>
	///	</code>
	///	
	///	<list type="table">
	///		<listheader>
	///			<term>Tag</term>
	///			<description>Description</description>
	///		</listheader>
	///		<item>
	///			<term>DelayBeforeCheck</term>
	///			<description>The time to wait before checking the event log (seconds)</description>
	///		</item>
	///		<item>
	///			<term>Machine</term>
	///			<description>The name of the machine where the event log should be checked, (optional, defaults to local machine)</description>
	///		</item>
	///		<item>
	///			<term>EventLog</term>
	///			<description>The event log to check, e.g. Application, System etc.</description>
	///		</item>
	///		<item>
	///			<term>Source</term>
	///			<description>The event log source, e.g. BizTalk Server 2004</description>
	///		</item>
	///		<item>
	///			<term>Type</term>
	///			<description>The type of the event log entry, e.g. Error, Warning, Info.</description>
	///		</item>
	///		<item>
	///			<term>EventId</term>
	///			<description>The ID of the event to look for <para>(optional)</para></description>
	///		</item>
	///		<item>
	///			<term>ValidationRegex</term>
	///			<description>Regular expression used to check the event message.</description>
	///		</item>
	///		<item>
	///			<term>FailIfFound</term>
	///			<description>Flag to indicate whether the test step should fail if the event log entry is not present.</description>
	///		</item>
	///	</list>
	///	</remarks>	

	public class EventLogCheckStep : ITestStep
	{
		/// <summary>
		/// ITestStep.Execute() implementation
		/// </summary>
		/// <param name='testConfig'>The Xml fragment containing the configuration for this test step</param>
		/// <param name='context'>The context for the test, this holds state that is passed beteen tests</param>
		public void Execute(XmlNode testConfig, Context context)
		{
			int delayBeforeCheck = context.ReadConfigAsInt32(testConfig, "DelayBeforeCheck", true);
			if ( delayBeforeCheck > 0 )
			{
				context.LogInfo("Waiting for {0} seconds before checking the event log.", delayBeforeCheck);
				System.Threading.Thread.Sleep(delayBeforeCheck*1000);
			}

			string machine = context.ReadConfigAsString(testConfig, "Machine", true);
			if ( (null == machine ) || (machine.Length == 0) )
			{
				machine = Environment.MachineName;			
			}

			int eventId = context.ReadConfigAsInt32(testConfig, "EventId", true);
			string eventLog = context.ReadConfigAsString(testConfig, "EventLog");
			string source = context.ReadConfigAsString(testConfig, "Source");
			string type = context.ReadConfigAsString(testConfig, "Type");
			EventLogEntryType entryType = (EventLogEntryType) Enum.Parse(typeof(EventLogEntryType), type, true);
			XmlNodeList validationNodes = testConfig.SelectNodes("ValidationRegex");

			// work out the local cutoff time on the machine - we have to allow a couple of minutes on the first
			// test to account for the fact that times may be different on the machines
			DateTime cutOffTime = context.TestCaseStart - TimeSpan.FromMinutes(2);
			
			bool found = false;
			using (EventLog log = new EventLog(eventLog, machine))
            {
                EventLogEntryCollection entries = log.Entries;
        
                context.LogInfo("Scanning {0} event log entries from log '{1}' on machine '{2}'.", entries.Count, eventLog, machine);
                for (int i = entries.Count - 1; i >= 0; i--)
                {
                    EventLogEntry entry = entries[i];
                    if (entry.TimeGenerated < cutOffTime)
                    {
						found = false;
                        break;
                    }						

					// Note: EventId is optional...
                    if ( ((entry.Source == source) && (entry.EntryType == entryType)) && 
						 (((eventId > 0) && (entry.InstanceId == eventId)) || (eventId == 0)) )
                    {
                        foreach (XmlNode validationNode in validationNodes)
                        {
                            string matchPattern = validationNode.InnerText;
                            Match match = Regex.Match(entry.Message, matchPattern);

							if (match.Success)
							{
								found = true;
								context.LogInfo("Successfully matched event log entry generated at '{0}'.", entry.TimeGenerated);
								context.LogData("Event log entry.", entry.Message);
								break;
							}
							else
							{
								found = false;
							}
                        }
                    }

					if (found)
					{
						break;
					}
                }
            }

			// See if we wanted to find it
			bool failIfFound = context.ReadConfigAsBool(testConfig, "FailIfFound", true);

			// Ccheck that its ok
			if (!failIfFound && !found)
			{
				throw new ApplicationException("Failed to find expected event log entry.");
			}
			else if (failIfFound && found)
			{
				throw new ApplicationException("Found event log entry which should not be present.");
			}
		}
	}
}
